﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;

using SNCS.Interop;

namespace SNCS.Interop.Edge
{
  public class Startup
  {
    private class ClientContainer
    {
      public bool inUse { get; set; }
      public Client client { get; set; }
      public DateTime lastAccess { get; set; }
    }

    private class ConnectProperties
    {
      private string _user = "user";
      public string user { get { return _user; } set { _user = value; } }
      private string _pass = "pass";
      public string pass { get { return _pass; } set { _pass = value; } }
      private string _serviceName = "SNCS";
      public string serviceName { get { return _serviceName; } set { _serviceName = value; } }
      private string _server = "localhost";
      public string server { get { return _server; } set { _server = value; } }
      private int _port = 12345;
      public int port { get { return _port; } set { _port = value; } }
      private bool _auth = true;
      public bool auth { get { return _auth; } set { _auth = value; } }
    }

    public class CloseProperties
    {
      public string id { get; set; }
    }

    public class SetPrivateValueProperties
    {
      public string id { get; set; }
      public string db { get; set; }
      public string name { get; set; }
      public string value { get; set; }
    }

    public class GetPrivateValueProperties
    {
      public string id { get; set; }
      public string db { get; set; }
      public string name { get; set; }
    }

    public class SetValueProperties
    {
      public string id { get; set; }
      public string name { get; set; }
      public string value { get; set; }
    }

    public class GetValueProperties
    {
      public string id { get; set; }
      public string name { get; set; }
    }

    public class GetDBProperties
    {
      public string id { get; set; }
      public string name { get; set; }
      public bool create { get; set; }
    }

    private static Dictionary<string, ClientContainer> _instances = new Dictionary<string, ClientContainer>();
    private static JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
    private static System.Timers.Timer t = new System.Timers.Timer(10000);

    private static Client getInstance(string id)
    {
      try
      {
        lock (_instances)
        {
          _instances[id].lastAccess = DateTime.Now;
          return _instances[id].client;
        }
      }
      catch (Exception ex)
      {
        throw new Exception("The client connection timed out.");
      }
    }

    private static void t_Elapsed(object Sender, System.Timers.ElapsedEventArgs e)
    {
      lock (_instances)
      {
        foreach (var kv in _instances)
        {
          if (Math.Abs((kv.Value.lastAccess - DateTime.Now).TotalMinutes) > 10)
          {
            kv.Value.client.Close();
            _instances.Remove(kv.Key);
          }
        }
        if (_instances.Count < 1)
        {
          t.Stop();
        }
      }
    }

    static Startup()
    {
      t.Elapsed += t_Elapsed;
      t.AutoReset = true;
    }

    public async Task<object> Connect(object input)
    {
      lock (_instances)
      {
        bool found = false;
        Client c = null;
        string _guid = "";

        if (_instances.Count < 1)
        {
          t.Start();
        }
        else
        {
          foreach (var kv in _instances)
          {
            if (!kv.Value.inUse)
            {
              _guid = kv.Key;
              found = true;
              break;
            }
          }
        }

        ConnectProperties cp = jsSerializer.ConvertToType<ConnectProperties>(input);

        if (!found)
        {
          _guid = Guid.NewGuid().ToString();
          c = new Client();
        }

        c.Connect(cp.server, cp.port, cp.serviceName);

        if (cp.auth)
        {
          c.Authenticate(cp.user, cp.pass);
        }

        if (!found)
        {
          _instances.Add(_guid.ToString(), new ClientContainer() { client = c, lastAccess = DateTime.Now, inUse = true });
        }
        else
        {
          _instances[_guid].client = c;
          _instances[_guid].lastAccess = DateTime.Now;
          _instances[_guid].inUse = true;
        }
        return _guid;
      }
    }

    public async Task<object> GetDB(object input)
    {
      GetDBProperties gp = jsSerializer.ConvertToType<GetDBProperties>(input);
      return getInstance(gp.id).GetDB(gp.name, gp.create);
    }

    public async Task<object> DeleteDB(object input)
    {
      GetDBProperties gp = jsSerializer.ConvertToType<GetDBProperties>(input);
      return getInstance(gp.id).DeleteDB(gp.name);
    }

    public async Task<object> GetPrivateValue(object input)
    {
      GetPrivateValueProperties gp = jsSerializer.ConvertToType<GetPrivateValueProperties>(input);
      return getInstance(gp.id).GetPrivateValue(gp.db, gp.name);
    }

    public async Task<object> SetPrivateValue(object input)
    {
      SetPrivateValueProperties sp = jsSerializer.ConvertToType<SetPrivateValueProperties>(input);
      return getInstance(sp.id).SetPrivateValue(sp.db, sp.name, sp.value);
    }

    public async Task<object> DelPrivateValue(object input)
    {
      GetPrivateValueProperties gp = jsSerializer.ConvertToType<GetPrivateValueProperties>(input);
      return getInstance(gp.id).DelPrivateValue(gp.db, gp.name);
    }

    public async Task<object> ListDBs(object input)
    {
      GetPrivateValueProperties gp = jsSerializer.ConvertToType<GetPrivateValueProperties>(input);
      return (IDictionary<string, object>)(getInstance(gp.id).ListDBs(gp.db));
    }

    public async Task<object> ListValues(object input)
    {
      GetPrivateValueProperties gp = jsSerializer.ConvertToType<GetPrivateValueProperties>(input);
      return getInstance(gp.id).ListValues(gp.name);
    }

    public async Task<object> ListPrivateValues(object input)
    {
      GetPrivateValueProperties gp = jsSerializer.ConvertToType<GetPrivateValueProperties>(input);
      return getInstance(gp.id).ListPrivateValues(gp.db, gp.name);
    }

    public async Task<object> GetValue(object input)
    {
      GetValueProperties gp = jsSerializer.ConvertToType<GetValueProperties>(input);
      return getInstance(gp.id).GetValue(gp.name);
    }

    public async Task<object> SetValue(object input)
    {
      SetValueProperties sp = jsSerializer.ConvertToType<SetValueProperties>(input);
      return getInstance(sp.id).SetValue(sp.name, sp.value);
    }

    public async Task<object> DelValue(object input)
    {
      GetValueProperties gp = jsSerializer.ConvertToType<GetValueProperties>(input);
      return getInstance(gp.id).DelValue(gp.name);
    }

    public async Task<object> GetConnected(object input)
    {
      CloseProperties gp = jsSerializer.ConvertToType<CloseProperties>(input);
      return getInstance(gp.id).Connected;
    }

    public async Task<object> GetConnectedServer(object input)
    {
      CloseProperties gp = jsSerializer.ConvertToType<CloseProperties>(input);
      return getInstance(gp.id).ConnectedServer;
    }

    public async Task<object> GetConnectedServerPort(object input)
    {
      CloseProperties gp = jsSerializer.ConvertToType<CloseProperties>(input);
      return getInstance(gp.id).ConnectedServerPort;
    }

    public async Task<object> Close(object input)
    {
      lock (_instances)
      {
        CloseProperties cp = jsSerializer.ConvertToType<CloseProperties>(input);
        _instances[cp.id].inUse = false;
        getInstance(cp.id).Close();
        return true;
      }
    }
  }
}
